/**
 * Copyright [2019] [LiBo/Alex of copyright liboware@gmail.com ]
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package report.delayed.bootstrap;

import com.google.common.collect.Maps;
import com.lhy.report.delayed.annotation.DelayedQueueExceptionHandler;
import com.lhy.report.delayed.annotation.DelayedQueueListener;
import com.lhy.report.delayed.context.DelayedBootstrapRunnable;
import com.lhy.report.delayed.context.DelayedThreadPoolSupport;
import com.lhy.report.delayed.impl.EventExecutableInvokerListener;
import com.lhy.report.delayed.listener.DelayedExceptionHandler;
import com.lhy.report.delayed.listener.ExecutableExceptionHandler;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.logging.log4j.util.Strings;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

/**
 * @project-name:wiz-shrding-framework
 * @package-name:com.wiz.sharding.framework.boot.starter.redisson.delayed.bootstrap
 * @author:LiBo/Alex
 * @create-date:2021-08-11 17:16
 * @copyright:libo-alex4java
 * @email:liboware@gmail.com
 * @description:
 */

@Slf4j
public class DelayedBootstrapInitializer {


    @Setter
    @Getter
    @DelayedQueueListener(value="delayedListenerContextMap")
    Map<String, EventExecutableInvokerListener> delayedListenerContextMap = Maps.newHashMap();



    @Setter
    @Getter
    @DelayedQueueExceptionHandler(value="delayedExceptionHandlerMap")
    Map<String, DelayedExceptionHandler> delayedExceptionHandlerMap = Maps.newHashMap();



    /**
     * 初始化操作机制控制
     */
    public void init(){
        log.info("Start initialization loading and complete all relevant delay " +
                "queues in the system to listen for context interface service data : {}",delayedListenerContextMap);
        log.info("Start to complete thread task allocation, and allocate resources for each group's listener and task queue");
        if(MapUtils.isEmpty(delayedListenerContextMap)){
            log.info("No task listening information was found. In the context managed by springcontext, " +
                    "please check whether there are any interfaces about implementation" +
                    "com.wiz.sharding.framework.boot.starter.redisson.delayed.impl.EventExecutableInvokerListener，以及相关@DelayedQueueListener");
            return ;
        }
        log.info("Start production related listening binding mechanism");
        Map<String,List<EventExecutableInvokerListener>> getAnnotationMetadataGroup =
                delayedListenerContextMap.values().stream().collect(Collectors.groupingBy(DelayedBootstrapInitializer::getAnnotationMetadataGroupListener));

        log.info("Start initializing the relevant exception information handling mechanism");
        Map<String,List<DelayedExceptionHandler>> delayedExceptionHandlerMapGroup =
                delayedExceptionHandlerMap.values().stream().collect(Collectors.groupingBy(DelayedBootstrapInitializer::getAnnotationMetadataGroupExceptionHandler));


        if(MapUtils.isNotEmpty(getAnnotationMetadataGroup)){
            Executor executor = DelayedThreadPoolSupport.getTaskRecycleThread();
            log.info("Start resource allocation mechanism");
            //推荐同一个组里面采用一个线程池进行处理机制
            getAnnotationMetadataGroup.entrySet().forEach(param->{
                log.info("Initialize threading mechanism {}：",param.getValue());
                executor.execute(new DelayedBootstrapRunnable(param.getKey(),param.getValue(),
                        DelayedBootstrapInitializer.getExecutorByGroup(param.getValue()),
                        new ExecutableExceptionHandler(delayedExceptionHandlerMapGroup.get(param.getKey()))));
            });
        }else{
            log.warn("Resource conversion failed! Unable to execute resource execution mechanism！");
        }
    }


    /**
     *
     * @param eventExecutableInvokerListener
     * @return
     */
    public static String getAnnotationMetadataGroupListener(EventExecutableInvokerListener eventExecutableInvokerListener){
        return getAnnotationMetadataGroup(eventExecutableInvokerListener,DelayedQueueListener.class);
    }


    public static String getAnnotationMetadataGroupExceptionHandler( DelayedExceptionHandler delayedExceptionHandler){
        return getAnnotationMetadataGroup(delayedExceptionHandler,DelayedQueueExceptionHandler.class);
    }


    /**
     * 获取相关的组信息
     * @param object
     * @return
     */
    public static String getAnnotationMetadataGroup(Object object,Class delayedQueueListenerClass){
        Object annotationInstance = object.getClass().getAnnotation(delayedQueueListenerClass);
        if(annotationInstance instanceof DelayedQueueListener) {
            DelayedQueueListener delayedQueueListener = (DelayedQueueListener)annotationInstance;
            if(Objects.isNull(annotationInstance)){
                return Strings.EMPTY;
            }else{
                return delayedQueueListener.group();
            }
        }
        else if(annotationInstance instanceof DelayedQueueExceptionHandler) {
            DelayedQueueExceptionHandler delayedExceptionHandler = (DelayedQueueExceptionHandler)annotationInstance;
            if(Objects.isNull(annotationInstance)){
                return Strings.EMPTY;
            }else{
                return delayedExceptionHandler.group();
            }
        }
        return Strings.EMPTY;
    }


    /**
     * 执行线程组机制
     * @return
     */
    public static Executor getExecutorByGroup(List<EventExecutableInvokerListener> eventExecutableInvokerListeners){
        return eventExecutableInvokerListeners.stream().map(EventExecutableInvokerListener::getExecutor).
                filter(Objects::nonNull).findAny().orElse(null);
    }

}
